export interface PerformanceMetrics {
  // Core Web Vitals
  fcp?: number  // First Contentful Paint
  lcp?: number  // Largest Contentful Paint
  fid?: number  // First Input Delay
  cls?: number  // Cumulative Layout Shift
  ttfb?: number // Time to First Byte
  
  // Custom metrics
  pluginLoadTime?: number
  routeChangeTime?: number
  apiResponseTime?: number
  bundleLoadTime?: number
  
  // Navigation timing
  domContentLoaded?: number
  loadComplete?: number
  
  // Resource timing
  resourceCount?: number
  totalResourceSize?: number
}

interface CustomPerformanceEntry {
  name: string
  value: number
  timestamp: number
  metadata?: Record<string, any>
}

class PerformanceMonitor {
  private metrics: PerformanceMetrics = {}
  private entries: CustomPerformanceEntry[] = []
  private observer?: PerformanceObserver
  private clsValue = 0
  private clsEntries: any[] = []

  constructor() {
    this.setupObserver()
    this.measureCoreWebVitals()
    this.measureNavigationTiming()
  }

  private setupObserver() {
    if (!('PerformanceObserver' in window)) {
      console.warn('PerformanceObserver not supported')
      return
    }

    try {
      this.observer = new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
          this.handlePerformanceEntry(entry)
        }
      })

      // Observe different entry types
      const entryTypes = ['navigation', 'paint', 'largest-contentful-paint', 'first-input']
      entryTypes.forEach(type => {
        try {
          this.observer!.observe({ type, buffered: true })
        } catch (e) {
          console.warn(`Cannot observe ${type}:`, e)
        }
      })
    } catch (error) {
      console.warn('Failed to setup PerformanceObserver:', error)
    }
  }

  private handlePerformanceEntry(entry: PerformanceEntry) {
    switch (entry.entryType) {
      case 'navigation':
        const navEntry = entry as PerformanceNavigationTiming
        this.metrics.ttfb = navEntry.responseStart - navEntry.requestStart
        this.metrics.domContentLoaded = navEntry.domContentLoadedEventEnd - navEntry.fetchStart
        this.metrics.loadComplete = navEntry.loadEventEnd - navEntry.fetchStart
        break
      
      case 'paint':
        if (entry.name === 'first-contentful-paint') {
          this.metrics.fcp = entry.startTime
          this.addEntry('fcp', entry.startTime)
        }
        break
      
      case 'largest-contentful-paint':
        this.metrics.lcp = entry.startTime
        this.addEntry('lcp', entry.startTime)
        break
        
      case 'first-input':
        const fidEntry = entry as any
        if (fidEntry.processingStart && fidEntry.startTime) {
          this.metrics.fid = fidEntry.processingStart - fidEntry.startTime
          this.addEntry('fid', this.metrics.fid)
        }
        break
    }
  }

  private measureCoreWebVitals() {
    // CLS measurement
    if ('PerformanceObserver' in window) {
      try {
        new PerformanceObserver((list) => {
          for (const entry of list.getEntries()) {
            const layoutShift = entry as any
            if (!layoutShift.hadRecentInput) {
              this.clsValue += layoutShift.value
              this.clsEntries.push(layoutShift)
              this.metrics.cls = this.clsValue
            }
          }
        }).observe({ type: 'layout-shift', buffered: true })
      } catch (e) {
        console.warn('Cannot observe layout-shift:', e)
      }
    }
  }

  private measureNavigationTiming() {
    if ('performance' in window && 'getEntriesByType' in performance) {
      const navEntries = performance.getEntriesByType('navigation') as PerformanceNavigationTiming[]
      if (navEntries.length > 0) {
        const entry = navEntries[0]
        this.metrics.ttfb = entry.responseStart - entry.requestStart
        this.metrics.domContentLoaded = entry.domContentLoadedEventEnd - entry.fetchStart
        this.metrics.loadComplete = entry.loadEventEnd - entry.fetchStart
      }
    }
  }

  measurePluginLoad(pluginName: string, startTime: number) {
    const loadTime = performance.now() - startTime
    this.metrics.pluginLoadTime = loadTime
    this.addEntry('plugin-load', loadTime, { pluginName })
    
    console.log(`Plugin ${pluginName} loaded in ${loadTime.toFixed(2)}ms`)
  }

  measureRouteChange(route: string, startTime: number) {
    const changeTime = performance.now() - startTime
    this.metrics.routeChangeTime = changeTime
    this.addEntry('route-change', changeTime, { route })
    
    console.log(`Route change to ${route} took ${changeTime.toFixed(2)}ms`)
  }

  measureApiCall(url: string, startTime: number, method = 'GET') {
    const responseTime = performance.now() - startTime
    this.metrics.apiResponseTime = responseTime
    this.addEntry('api-call', responseTime, { url, method })
    
    console.log(`API call ${method} ${url} took ${responseTime.toFixed(2)}ms`)
  }

  measureBundleLoad(bundleName: string, startTime: number) {
    const loadTime = performance.now() - startTime
    this.metrics.bundleLoadTime = loadTime
    this.addEntry('bundle-load', loadTime, { bundleName })
    
    console.log(`Bundle ${bundleName} loaded in ${loadTime.toFixed(2)}ms`)
  }

  measureResourceTiming() {
    if ('performance' in window && 'getEntriesByType' in performance) {
      const resources = performance.getEntriesByType('resource') as PerformanceResourceTiming[]
      this.metrics.resourceCount = resources.length
      this.metrics.totalResourceSize = resources.reduce((total, resource) => {
        return total + (resource.transferSize || 0)
      }, 0)
    }
  }

  private addEntry(name: string, value: number, metadata?: Record<string, any>) {
    this.entries.push({
      name,
      value,
      timestamp: Date.now(),
      metadata
    })

    // Keep only last 100 entries to prevent memory leaks
    if (this.entries.length > 100) {
      this.entries = this.entries.slice(-100)
    }
  }

  getMetrics(): PerformanceMetrics {
    this.measureResourceTiming()
    return { ...this.metrics }
  }

  getEntries(): CustomPerformanceEntry[] {
    return [...this.entries]
  }

  getCoreWebVitals() {
    return {
      fcp: this.metrics.fcp,
      lcp: this.metrics.lcp,
      fid: this.metrics.fid,
      cls: this.metrics.cls,
      ttfb: this.metrics.ttfb
    }
  }

  getPerformanceScore() {
    const vitals = this.getCoreWebVitals()
    let score = 100
    
    // FCP scoring (good: <1.8s, needs improvement: 1.8-3s, poor: >3s)
    if (vitals.fcp) {
      if (vitals.fcp > 3000) score -= 20
      else if (vitals.fcp > 1800) score -= 10
    }
    
    // LCP scoring (good: <2.5s, needs improvement: 2.5-4s, poor: >4s)
    if (vitals.lcp) {
      if (vitals.lcp > 4000) score -= 25
      else if (vitals.lcp > 2500) score -= 15
    }
    
    // FID scoring (good: <100ms, needs improvement: 100-300ms, poor: >300ms)
    if (vitals.fid) {
      if (vitals.fid > 300) score -= 20
      else if (vitals.fid > 100) score -= 10
    }
    
    // CLS scoring (good: <0.1, needs improvement: 0.1-0.25, poor: >0.25)
    if (vitals.cls) {
      if (vitals.cls > 0.25) score -= 25
      else if (vitals.cls > 0.1) score -= 15
    }
    
    return Math.max(0, score)
  }

  sendMetrics() {
    const metrics = this.getMetrics()
    const entries = this.getEntries()
    
    // Send to analytics service
    fetch('/api/metrics', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        metrics,
        entries: entries.slice(-20), // Send only recent entries
        timestamp: Date.now(),
        userAgent: navigator.userAgent,
        url: window.location.href
      })
    }).catch(() => {
      // Silent failure for metrics
    })
  }

  reset() {
    this.metrics = {}
    this.entries = []
    this.clsValue = 0
    this.clsEntries = []
  }
}

export const performanceMonitor = new PerformanceMonitor()

// Send metrics on page unload
window.addEventListener('beforeunload', () => {
  performanceMonitor.sendMetrics()
})

// Send metrics periodically
setInterval(() => {
  performanceMonitor.sendMetrics()
}, 30000) // Every 30 seconds
